package product

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/util/gconv"
	"home-butler/internal/consts"
	"home-butler/internal/dao"
	"home-butler/internal/model"
	"home-butler/internal/model/do"
	"home-butler/internal/model/entity"
	"home-butler/internal/service"
	"home-butler/pkg/taos"
	"home-butler/utility"
	"strings"
)

func init() {
	service.RegisterProduct(New())
}

type sProduct struct{}

func New() service.IProduct {
	return &sProduct{}
}

func (s sProduct) ProductList(ctx context.Context, req *model.ProductReq) (total int, res []*model.ProductRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.Product.Ctx(ctx)
		if req.Name != "" {
			m = m.WhereLike(dao.Product.Columns().Name, "%"+req.Name+"%")
		}
		total, err = m.Count()
		if err != nil {
			panic(err)
		}
		err = m.Page(req.Page, req.Limit).Scan(&res)
		if err != nil {
			panic(err)
		}
	})
	return
}

func (s sProduct) CreateProduct(ctx context.Context, req *model.ProductCreateReq) (res *model.ProductRes, err error) {
	product := do.Product{}
	if err = gconv.Struct(req, &product); err != nil {
		panic(err)
	}
	product.Id = utility.GID.Generate().Int64()
	if _, err = dao.Product.Ctx(ctx).Insert(product); err != nil {
		panic(err)
	}
	return
}

func (s sProduct) UpdateProduct(ctx context.Context, req *model.ProductUpdateReq) (res *model.ProductRes, err error) {
	product := do.Product{}
	if err = gconv.Struct(req, &product); err != nil {
		panic(err)
	}
	if _, err = dao.Product.Ctx(ctx).Update(product); err != nil {
		panic(err)
	}
	return
}

func (s sProduct) DelProduct(ctx context.Context, req *model.ProductDeleteReq) (res *model.ProductRes, err error) {
	if _, err = dao.Product.Ctx(ctx).Delete(g.Map{
		dao.Product.Columns().Id: req.Id,
	}); err != nil {
		panic(err)
	}
	return
}

func (s sProduct) ProductAddMetadata(ctx context.Context, req *model.ProductAddMetadataReq) (res *model.ProductRes, err error) {
	product := new(entity.Product)
	dao.Product.Ctx(ctx).Where(dao.Product.Columns().Id, req.ProductId).Scan(&product)
	metadata := model.ProductMetadataModel{}
	if product.Metadata != "" {
		err = json.Unmarshal([]byte(product.Metadata), &metadata)
		if err != nil {
			panic(err)
		}
	} else {
		metadata.Events = make([]model.ProductMetadata, 0)
		metadata.Properties = make([]model.ProductMetadata, 0)
		metadata.Services = make([]model.ProductMetadata, 0)
	}

	if req.Type == "propertie" {
		// 验证属性是否重复
		for _, v := range metadata.Properties {
			if v.Identifier == req.Properties.Identifier {
				panic("属性标识符重复")
			}
		}
		productMetadata := append(metadata.Properties, req.Properties)
		metadata.Properties = productMetadata
	} else if req.Type == "event" {
		// 验证属性是否重复
		for _, v := range metadata.Events {
			if v.Identifier == req.Events.Identifier {
				panic("事件标识符重复")
			}
		}
		productMetadata := append(metadata.Events, req.Events)
		metadata.Events = productMetadata
	} else if req.Type == "service" {
		// 验证属性是否重复
		for _, v := range metadata.Services {
			if v.Identifier == req.Services.Identifier {
				panic("服务标识符重复")
			}
		}
		productMetadata := append(metadata.Services, req.Services)
		metadata.Services = productMetadata
	}

	metadataBytes, err := json.Marshal(metadata)
	if err != nil {
		panic(err)
	}
	_, err = dao.Product.Ctx(ctx).Data(dao.Product.Columns().Metadata, string(metadataBytes)).Where(dao.Product.Columns().Id, req.ProductId).Update()
	return
}

func (s sProduct) FindGetMetadata(ctx context.Context, id uint64) (key string, deviceType string, res *model.ProductMetadataModel, err error) {
	product := entity.Product{}
	dao.Product.Ctx(ctx).Where(dao.Product.Columns().Id, id).Scan(&product)

	key = product.Key
	deviceType = product.DeviceType
	if product.Metadata != "" {
		err = json.Unmarshal([]byte(product.Metadata), &res)
		if err != nil {
			panic(err)
		}
	}
	return
}

func (s sProduct) ReleasedProduct(ctx context.Context, req *model.ReleasedProductReq) (err error) {
	product := entity.Product{}
	err = dao.Product.Ctx(ctx).Where(dao.Product.Columns().Id, req.ProductId).Scan(&product)
	if err != nil {
		panic(err)
	}
	if err = g.DB().Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err := dao.Product.Ctx(ctx).Data(dao.Product.Columns().Status, 1).
			Where(dao.Product.Columns().Id, req.ProductId).
			Update()
		if err != nil {
			panic(err)
		}
		if product.MetadataTable == 1 {
			// TODO 生成物模型
			tableName := consts.TDB_TABLE_NAME_PREFIX + product.Key
			metadata := model.ProductMetadataModel{}
			columns := []string{"ts TIMESTAMP"}
			err = json.Unmarshal([]byte(product.Metadata), &metadata)
			if err != nil {
				panic(err)
			}
			for _, v := range metadata.Properties {
				columns = append(columns, utility.Column(v.DataType, v.Identifier, "", 0))
			}
			sql := fmt.Sprintf("CREATE STABLE IF NOT EXISTS %s.%s (%s) TAGS(device NCHAR(50))", consts.TDB_NAME, tableName, strings.Join(columns, ","))
			fmt.Println(sql)
			taos, err := taos.DB()
			if err != nil {
				panic(err)
			}
			err = taos.CreateTable(sql)
			if err != nil {
				panic(err)
			}

		}
		return nil
	}); err != nil {
		panic(err)
	}
	return
}
